#ifndef _ONLINE_
#define _ONLINE_

#include "RakNet.h"
#include <string>

#ifndef RAK_PORT
	#define RAK_PORT 44893
#endif

#ifndef TIME_OUT
	#define TIME_OUT 5*1000 // 5 secs by 1000 milisecs.
#endif

//RakNet developed function:
unsigned char GetPacketIdentifier(Packet *p)
{
	if (p==0)
		return 255;

	if ((unsigned char)p->data[0] == ID_TIMESTAMP)
	{
		assert(p->length > sizeof(unsigned char) + sizeof(unsigned long));
		return (unsigned char) p->data[sizeof(unsigned char) + sizeof(unsigned long)];
	}
	else
		return (unsigned char) p->data[0];
}
RakPeerInterface * Connection;
SystemAddress Other;
bool Server = false, online = false, waiting = false;
bool _up,_down,_left,_right;
char * tmp = new char[30]; // Temporal buffer;

void CloseServer()
{
	Player * P1 = (Player*)Player1Pointer;
	P1->SetKeyboardControlled(true);
	Player * P2 = (Player*)Player2Pointer;
	P2->SetKeyboardControlled(true);
	if (online)
	{
		Connection->Shutdown(300);
		RakNetworkFactory::DestroyRakPeerInterface(Connection);
		Server = false;
		online = false;
		waiting = false;
	}
}
bool InitServer()
{
	if(online)
		return true;
	Connection = RakNetworkFactory::GetRakPeerInterface();
	Connection->SetIncomingPassword("ZekiBallPassword", (int)strlen("ZekiBallPassword"));
	Connection->SetTimeoutTime(TIME_OUT,UNASSIGNED_SYSTEM_ADDRESS);
	SocketDescriptor socketDescriptor(RAK_PORT,0);
	bool b = false;
	b = Connection->Startup(2, 30, &socketDescriptor, 1);
	if (!b)
	{
		CloseServer();
		b = InitServer();
		if (!b) return false;
	}
	Connection->SetMaximumIncomingConnections(2);
	Connection->SetOccasionalPing(true);
	Connection->SetUnreliableTimeout(TIME_OUT);
	waiting = true;
	Server = true;
	online = true;
	return true;
}
bool JoinServer(const char * ip)
{
	if(online)
		return true;
	if(SystemAddress(ip,RAK_PORT) == SystemAddress("127.0.0.1",RAK_PORT))
		return false;
	Server = false;
	Connection = RakNetworkFactory::GetRakPeerInterface();
	SocketDescriptor socketDescriptor(RAK_PORT,0);
	Connection->Startup(2, 30, &socketDescriptor, 1 );
	Connection->SetOccasionalPing(true);
	online = Connection->Connect(ip, RAK_PORT, "ZekiBallPassword", (int)strlen("ZekiBallPassword"));	
	if (!online)
	{
		CloseServer();
		online = Connection->Connect(ip, RAK_PORT, "ZekiBallPassword", (int)strlen("ZekiBallPassword"));
	}
	waiting = true;
	return online;
}

void UpdateObjects(bool IsServer,Packet * p)
{
	std::string PData = (char*)p->data;
	if (IsServer) // Just need to update player 2
	{
		b2Vec2 P2Pos;
		Player * P2 = (Player*)Player2Pointer;
		
		// Player keys
		_up = PData.substr(0,1).compare("1") == 0;
		PData = PData.substr(PData.find("||")+2,PData.length());
		_left = PData.substr(0,1).compare("1") == 0;
		PData = PData.substr(PData.find("||")+2,PData.length());
		_down = PData.substr(0,1).compare("1") == 0;
		PData = PData.substr(PData.find("||")+2,PData.length());
		_right = PData.substr(0,1).compare("1") == 0;
		PData = PData.substr(PData.find("||")+2,PData.length());

		// Player pos
		P2Pos.x = FLOAT(atof(PData.substr(0,PData.find("||")-1).c_str()));
		PData = PData.substr(PData.find("||")+2,PData.length());
		P2Pos.y = FLOAT(atof(PData.substr(0,PData.find("||")-1).c_str()));
		PData = PData.substr(PData.find("||")+2,PData.length());
		P2->SetPosition(P2Pos);
	}
	else // Need to update ball and player 1
	{
		
		b2Vec2 P1Pos;
		Player * P1 = (Player*)Player1Pointer;
		
		// Player keys
		_up = PData.substr(0,1).compare("1") == 0;
		PData = PData.substr(PData.find("||")+2,PData.length());
		_left = PData.substr(0,1).compare("1") == 0;
		PData = PData.substr(PData.find("||")+2,PData.length());
		_down = PData.substr(0,1).compare("1") == 0;
		PData = PData.substr(PData.find("||")+2,PData.length());
		_right = PData.substr(0,1).compare("1") == 0;
		PData = PData.substr(PData.find("||")+2,PData.length());

		// Player pos
		P1Pos.x = FLOAT(atof(PData.substr(0,PData.find("||")-1).c_str()));
		PData = PData.substr(PData.find("||")+2,PData.length());
		P1Pos.y = FLOAT(atof(PData.substr(0,PData.find("||")-1).c_str()));
		PData = PData.substr(PData.find("||")+2,PData.length());
		P1->SetPosition(P1Pos);

		// Ball pos
		Entity * _BA = (Entity*)BallPointer;
		P1Pos.x = FLOAT(atof(PData.substr(0,PData.find("||")-1).c_str()));
		PData = PData.substr(PData.find("||")+2,PData.length());
		P1Pos.y = FLOAT(atof(PData.substr(0,PData.find("||")-1).c_str()));
		PData = PData.substr(PData.find("||")+2,PData.length());
		_BA->SetPosition(P1Pos);
		
	}
}
void ReplyPacket(bool IsServer)
{
	if (IsServer)
	{
		Player * P1 = (Player*)Player1Pointer;
		Entity * _BA = (Entity*)BallPointer;
		if(	  hge->Input_KeyDown(P1->GetKeyboardData().jump) || hge->Input_KeyUp(P1->GetKeyboardData().jump)
			||hge->Input_KeyDown(P1->GetKeyboardData().left) || hge->Input_KeyUp(P1->GetKeyboardData().left)
			||hge->Input_KeyDown(P1->GetKeyboardData().down) || hge->Input_KeyUp(P1->GetKeyboardData().down)
			||hge->Input_KeyDown(P1->GetKeyboardData().right) || hge->Input_KeyUp(P1->GetKeyboardData().right))
		{
			std::string sender = (hge->Input_GetKeyState(P1->GetKeyboardData().jump)) ? "1" : "0";
			sender.insert(sender.length(),"||");
			sender.insert(sender.length(),(hge->Input_GetKeyState(P1->GetKeyboardData().left)) ? "1" : "0");
			sender.insert(sender.length(),"||");
			sender.insert(sender.length(),(hge->Input_GetKeyState(P1->GetKeyboardData().down)) ? "1" : "0");
			sender.insert(sender.length(),"||");
			sender.insert(sender.length(),(hge->Input_GetKeyState(P1->GetKeyboardData().right)) ? "1" : "0");
			sender.insert(sender.length(),"||");

			sender.insert(sender.length(),FloatToString(P1->GetPosition().x,tmp));
			sender.insert(sender.length(),"||");
			sender.insert(sender.length(),FloatToString(P1->GetPosition().y,tmp));
			sender.insert(sender.length(),"||");

			sender.insert(sender.length(),FloatToString(_BA->GetPosition().x,tmp));
			sender.insert(sender.length(),"||");
			sender.insert(sender.length(),FloatToString(_BA->GetPosition().y,tmp));
			sender.insert(sender.length(),"||");

			Connection->Send(sender.c_str(),sender.length(),HIGH_PRIORITY,RELIABLE_ORDERED,0,UNASSIGNED_SYSTEM_ADDRESS,true);
		}
	}
	else
	{
		Player * P2 = (Player*)Player2Pointer;
		if(	  hge->Input_KeyDown(P2->GetKeyboardData().jump) || hge->Input_KeyUp(P2->GetKeyboardData().jump)
			||hge->Input_KeyDown(P2->GetKeyboardData().left) || hge->Input_KeyUp(P2->GetKeyboardData().left)
			||hge->Input_KeyDown(P2->GetKeyboardData().down) || hge->Input_KeyUp(P2->GetKeyboardData().down)
			||hge->Input_KeyDown(P2->GetKeyboardData().right) || hge->Input_KeyUp(P2->GetKeyboardData().right))
		{
			std::string sender = (hge->Input_GetKeyState(P2->GetKeyboardData().jump)) ? "1" : "0";
			sender.insert(sender.length(),"||");
			sender.insert(sender.length(),(hge->Input_GetKeyState(P2->GetKeyboardData().left)) ? "1" : "0");
			sender.insert(sender.length(),"||");
			sender.insert(sender.length(),(hge->Input_GetKeyState(P2->GetKeyboardData().down)) ? "1" : "0");
			sender.insert(sender.length(),"||");
			sender.insert(sender.length(),(hge->Input_GetKeyState(P2->GetKeyboardData().right)) ? "1" : "0");
			sender.insert(sender.length(),"||");

			sender.insert(sender.length(),FloatToString(P2->GetPosition().x,tmp));
			sender.insert(sender.length(),"||");
			sender.insert(sender.length(),FloatToString(P2->GetPosition().y,tmp));
			sender.insert(sender.length(),"||");

			Connection->Send(sender.c_str(),sender.length(),HIGH_PRIORITY,RELIABLE_ORDERED,0,UNASSIGNED_SYSTEM_ADDRESS,true);
		}
	}
}
void ProcessPacket()
{
	if (!online)
		return;
	Packet * p = Connection->Receive();
	if (!waiting)
		ReplyPacket(Server);
	if (p == 0)
	{
		Player * Pl = (Server) ? (Player*)Player2Pointer : (Player*)Player1Pointer;
		if(_up) Pl->SimulateKey(0);
		if(_down) Pl->SimulateKey(1);
		if(_left) Pl->SimulateKey(2);
		if(_right) Pl->SimulateKey(3);
		return;
	}
	if (waiting)
	{
		SetGameMode(MODE_ONLINE);
		Other = p->systemAddress;
		waiting = false;
	}
	if (p->systemAddress != Other)
		return;
	char pcki = GetPacketIdentifier(p);
	if (  pcki == ID_DISCONNECTION_NOTIFICATION
		||pcki == ID_ALREADY_CONNECTED 
		||pcki == ID_REMOTE_DISCONNECTION_NOTIFICATION 
		||pcki == ID_REMOTE_CONNECTION_LOST 
		||pcki == ID_REMOTE_NEW_INCOMING_CONNECTION 
		||pcki == ID_CONNECTION_BANNED 
		||pcki == ID_CONNECTION_ATTEMPT_FAILED 
		||pcki == ID_NO_FREE_INCOMING_CONNECTIONS 
		||pcki == ID_MODIFIED_PACKET 
		||pcki == ID_INVALID_PASSWORD 
		||pcki == ID_CONNECTION_REQUEST_ACCEPTED
		||pcki == ID_NEW_INCOMING_CONNECTION)
	{
		Connection->DeallocatePacket(p);
		return;
	}
	UpdateObjects(Server,p);
	Player * Pl = (Server) ? (Player*)Player2Pointer : (Player*)Player1Pointer;
	if(_up) Pl->SimulateKey(0);
	if(_down) Pl->SimulateKey(1);
	if(_left) Pl->SimulateKey(2);
	if(_right) Pl->SimulateKey(3);
	Connection->DeallocatePacket(p);
}
#endif